
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fastcgi.h>

#include "dualfifo.h"

static inline unsigned int fcgi_getParamLength( unsigned char *ptr, unsigned int *length );

FCGI_Header *fcgi_parse( dualfifo_t *fifo, unsigned int *contentLength )
{
	FCGI_Header header;
	FCGI_Header *data;
	unsigned int packetSize;

	if( dualfifo_bytesAvailable( fifo, DUALFIFO_MODE_PARSE ) < FCGI_HEADER_LEN )
	{
		// Not enough data to read the header, yet
		return NULL;
	}
	
	dualfifo_peek( fifo, DUALFIFO_MODE_PARSE, &header, sizeof( header ) );
	
	*contentLength = ( header.contentLengthB1 << 8 ) | header.contentLengthB0;

	packetSize = sizeof( header ) + *contentLength + header.paddingLength;
	
	if( dualfifo_bytesAvailable( fifo, DUALFIFO_MODE_PARSE ) < packetSize )
	{
		// Complete packet not available yet, try again later
		return NULL;
	}
	
	data = malloc( packetSize );
	if( data == NULL )
	{
		// Big trouble now.  Return NULL in the hope we get called again with more memory
		return NULL;
	}
	
	dualfifo_read( fifo, DUALFIFO_MODE_PARSE, data, packetSize );
	
	return data;
}

unsigned char * fcgi_getNextParam( unsigned char * ptr, 
		char **name, unsigned int *nameLen, char **value, unsigned int *valueLen )
{
	unsigned int nameLenBytes, valueLenBytes;
	
	nameLenBytes  = fcgi_getParamLength( ptr, nameLen );
	valueLenBytes = fcgi_getParamLength( ptr + nameLenBytes, valueLen );
	
	*name  = (char *) (ptr + nameLenBytes + valueLenBytes);
	*value = (char *) (*name + *nameLen);
	
	ptr = (unsigned char* ) *value + *valueLen;
	
	return ptr;
}

// TODO: inline is a C99 / GCC extension
static inline unsigned int fcgi_getParamLength( unsigned char *ptr, unsigned int *length )
{
	unsigned int bytes;	
	
	if( ( *ptr & 0x80) == 0 )
	{
		bytes = 1;
		*length =  *ptr;
	}
	else
	{
		unsigned char *b = (unsigned char *) ptr; 
		bytes = 4;
		*length = ((b[0] & 0x7f) << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; 
	}
	
	return bytes;
}

